﻿using Newtonsoft.Json;
using System;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Windows.UI.Xaml.Controls;
using Windows.Web.Http;

namespace doubanOAuth
{
    /// <summary>
    /// 获取access_token返回值
    /// </summary>
    public class AuthAccessToken
    {
        [JsonProperty("access_token")]
        public string Token { get; private set; }
        [JsonProperty("expires_in")]
        public int Expired { get; private set; }
        [JsonProperty("refresh_token")]
        public string RefreshToken { get; private set; }
        [JsonProperty("douban_user_id")]
        public string UserId { get; private set; }
    }

    public static  partial class API
    {
        /// <summary>
        /// 获取authorization_code
        /// </summary>
        /// <param name="name">用户名</param>
        /// <param name="pwd">密码</param>
        /// <param name="scope">(可选)申请权限的范围</param>
        /// <returns>authorization_code</returns>
        public static async Task<string> AuthGetCode(string name, string pwd, string scope = null)
        {
            UriBuilder ub = new UriBuilder(Common.AUTHGETCODE);
            Utilities.AddParam(ref ub, "client_id", Common.APIKey);
            Utilities.AddParam(ref ub, "redirect_uri", Common.RedirectUri);
            Utilities.AddParam(ref ub, "response_type", Common.RESPONSETYPE);
            Utilities.AddParam(ref ub, "scope", scope);
            StringBuilder builder = new StringBuilder();
            builder.Append("user_email", name);
            builder.Append("user_passwd", pwd);
            builder.Append("confirm", "授权");
            using (HttpWebResponse res = await Utilities.GetResponse(ub.ToString(), builder.ToString()))
            {
                int start = res.ResponseUri.Query.LastIndexOf("=");
                Common.AuthCode = res.ResponseUri.Query.Substring(start + 1);
                return Common.AuthCode;
            }
        }

        /// <summary>
        /// 获得Token
        /// </summary>
        /// <returns>AccessToken</returns>
        public static async Task<AuthAccessToken> AuthGetToken()
        {
            UriBuilder ub = new UriBuilder(Common.AUTHTOKENOP);
            Utilities.AddParam(ref ub, "client_id", Common.APIKey);
            Utilities.AddParam(ref ub, "client_secret", Common.Secret);
            Utilities.AddParam(ref ub, "redirect_uri", Common.RedirectUri);
            Utilities.AddParam(ref ub, "grant_type", Common.GRANTTYPE_GETCODE);
            Utilities.AddParam(ref ub, "code", Common.AuthCode);
            string result = await Utilities.RequestPost(ub.ToString());
            AuthAccessToken token = (AuthAccessToken)Utilities.JsonDeserialize<AuthAccessToken>(result);
            Common.Token = token.Token;
            Common.RefreshToken = token.RefreshToken;
            Common.UserId = token.UserId;
            return token;
        }

        /// <summary>
        /// 更换Token
        /// </summary>
        /// <returns>AccessToken</returns>
        public static async Task<AuthAccessToken> AuthRefreshToken()
        {
            UriBuilder ub = new UriBuilder(Common.AUTHTOKENOP);
            Utilities.AddParam(ref ub, "client_id", Common.APIKey);
            Utilities.AddParam(ref ub, "client_secret", Common.Secret);
            Utilities.AddParam(ref ub, "redirect_uri", Common.RedirectUri);
            Utilities.AddParam(ref ub, "grant_type", Common.GRANTTYPE_REFRESH);
            Utilities.AddParam(ref ub, "refresh_token", Common.RefreshToken);
            string result = await Utilities.RequestPost(ub.ToString());
            AuthAccessToken token = (AuthAccessToken)Utilities.JsonDeserialize<AuthAccessToken>(result);
            Common.Token = token.Token;
            Common.RefreshToken = token.RefreshToken;
            return token;
        }


        /// <summary>
        /// 授权
        /// </summary>
        /// <param name="webView">传入WebView用来Get/Post地址</param>
        /// <returns></returns>
        public static async Task<AuthAccessToken> Authorization(WebView webView)
        {
            AuthAccessToken result = null;
            bool hastoken = false;
            var url = CombineUrl(Common.AUTHGETCODE
                , new Tuple<string, string>("client_id", Common.APIKey)
                , new Tuple<string, string>("redirect_uri", Common.RedirectUri)
                , new Tuple<string, string>("response_type", Common.RESPONSETYPE)
                , new Tuple<string, string>("scope", Common.Scope)
                );
            webView.NavigationStarting += (sender, e) =>
            {
                var regex = new Regex(string.Format("{0}\\?code=.*", Common.RedirectUri));
                var match = regex.Match(e.Uri.AbsoluteUri);
                if (match.Success)
                {
                    webView.Stop();
                    Common.AuthCode = match.Value.Split('=')[1];

                    sender.NavigateWithHttpRequestMessage(GetTokenMessage());
                }
            };

            webView.NavigationCompleted += async (sender, e) =>
            {
                var regex = new Regex(
                        "{\"access_token\":\".*\",\"douban_user_name\":\".*\",\"douban_user_id\":\".*\",\"expires_in\":.*,\"refresh_token\":\".*\"}");
                if (e.Uri.AbsoluteUri == Common.AUTHTOKENOP)
                {
                    var html = await sender.InvokeScriptAsync("eval", new[] { "document.documentElement.outerHTML;" });
                    var match = regex.Match(html);
                    if (match.Success)
                    {
                        result = Utilities.JsonDeserialize<AuthAccessToken>(match.Value);

                        Common.Token = result.Token;
                        Common.RefreshToken = result.RefreshToken;
                        Common.UserId = result.UserId;
                    }
                    hastoken = true;
                }
            };

            webView.Navigate(new Uri(url));

            return await Task.Run<AuthAccessToken>(async () =>
            {
                while (!hastoken)
                {
                    await Task.Delay(500);
                }

                return result;
            });
        }


        /// <returns>请求令牌消息</returns>
        private static HttpRequestMessage GetTokenMessage()
        {
            var message = new Windows.Web.Http.HttpRequestMessage(HttpMethod.Post, new Uri(Common.AUTHTOKENOP));

            var parameters = CombineParameters(
                new Tuple<string, string>("client_id", Common.APIKey)
                , new Tuple<string, string>("client_secret", Common.Secret)
                , new Tuple<string, string>("redirect_uri", Common.RedirectUri)
                , new Tuple<string, string>("grant_type", Common.GRANTTYPE_GETCODE)
                , new Tuple<string, string>("code", Common.AuthCode)
                );

            var content = new HttpStringContent(parameters, Windows.Storage.Streams.UnicodeEncoding.Utf8);

            content.Headers.ContentType = new Windows.Web.Http.Headers.HttpMediaTypeHeaderValue("application/x-www-form-urlencoded");
            message.Content = content;

            return message;
        }


        /// <summary>
        /// 组合参数
        /// </summary>
        /// <param name="parameters">网址参数</param>
        /// <returns>组合完成的参数</returns>
        public static string CombineParameters(params Tuple<string, string>[] parameters)
        {
            return string.Join("&", parameters.Select(o => o.Item1 + "=" + o.Item2));
        }

        /// <summary>
        /// 组合网址和参数，一般用于Get
        /// </summary>
        /// <param name="url">首地址</param>
        /// <param name="parameters">参数</param>
        /// <returns>返回完整地址</returns>
        public static string CombineUrl(string url, params Tuple<string, string>[] parameters)
        {
            return url + "?" + CombineParameters(parameters);
        }
    }
}
